﻿
Imports std

Namespace directorystore
    Public Module directorystore
        Public Const filelistKeySuffix As String = ".$filelist$"
        Public Const filelistSeparator As Char = character.null
        Public Const pathSeperator As Char = network.pathSeperator

        Public Sub normalizeDirectory(ByRef d As String)
            kickRight(d, pathSeperator)
            d += pathSeperator
        End Sub

        Private Sub normalize(ByRef directory As String, ByRef talk As storoomTalk)
            normalizeDirectory(directory)
            normalizeTalk(talk)
        End Sub

        Public Function filelistkey(ByVal directory As String) As String
            Return directory + filelistKeySuffix
        End Function

        Private Function writefilelist(ByVal directory As String, ByVal files As [set](Of String), _
                                       Optional ByVal talk As storoomTalk = Nothing) As Boolean
            assert(Not files Is Nothing)
            normalize(directory, talk)
            Dim it As [set](Of String).iterator = Nothing
            Dim s As String = Nothing
            it = files.begin()
            While it <> files.end()
                s += (+it) + filelistSeparator
                it += 1
            End While
            If Not talk.modify(filelistkey(directory), New bytes(encoding.utf8().GetBytes(s))) Then
                raiseError("cannot modify filelist in directory " + directory + _
                           ", filelistkey is " + filelistkey(directory), errorHandle.errorType.warning)
                Return False
            Else
                Return True
            End If
        End Function

        Private Function writefilelist(ByVal directory As String, ByVal file As String, _
                                       Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            Dim s As [set](Of String) = Nothing
            If Not listfiles(directory, s, talk) Then
                raiseError(errorHandle.errorType.information, _
                           "cannot list files under directory ", directory, ", assume empty.")
                s = New [set](Of String)()
            End If
            assert(Not s Is Nothing)
            s.insert(file)
            Return writefilelist(directory, s, talk)
        End Function

        Private Sub confirmFilelistUnique(ByVal directory As String, ByRef files As vector(Of String), _
                                          Optional ByVal talk As storoomTalk = Nothing)
            assert(Not files Is Nothing)
            Dim fs As [set](Of String) = Nothing
            vector2set(files, fs)
            assert(Not fs Is Nothing)
            If fs.size() <> files.size() Then
                If Not writefilelist(directory, fs, talk) Then
                    raiseError("cannot update filelist under " + directory, errorHandle.errorType.warning)
                End If
                set2vector(fs, files)
            End If
        End Sub

        Private Function listfilesImpl(ByVal directory As String, ByRef files As String, _
                                       Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            Dim buff As bytes = Nothing
            If Not talk.read(filelistkey(directory), buff) Then
                raiseError("cannot read filelist in directory " + directory + _
                           ", filelistkey is " + filelistkey(directory), errorHandle.errorType.warning)
                Return False
            End If
            files = encoding.utf8().GetString(+buff)
            If isemptystring(files) Then
                raiseError("do not have files under " + directory)
            End If
            Return True
        End Function

        Public Function listfiles(ByVal directory As String, ByRef files As vector(Of String), _
                                  Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If files Is Nothing Then
                files = New vector(Of String)()
            Else
                files.clear()
            End If
            Dim s As String = Nothing
            If listfilesImpl(directory, s, talk) Then
                strsepar(s, filelistSeparator, files, True)
                sort(files.rawdata(), 0, files.size())
                confirmFilelistUnique(directory, files, talk)
                Return True
            Else
                Return False
            End If
        End Function

        Public Function listfiles(ByVal directory As String, ByRef files As [set](Of String), _
                                  Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If files Is Nothing Then
                files = New [set](Of String)()
            Else
                files.clear()
            End If
            Dim s As String = Nothing
            If listfilesImpl(directory, s, talk) Then
                strsepar(s, filelistSeparator, files, True)
                Return True
            Else
                Return False
            End If
        End Function

        Public Function readfile(ByVal directory As String, ByVal file As String, ByRef buff As bytes, _
                                 Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            If Not talk.read(directory + file, buff) Then
                raiseError("cannot read file " + directory + file, errorHandle.errorType.warning)
                Return False
            Else
                Return True
            End If
        End Function

        Public Function writefile(ByVal directory As String, ByVal file As String, ByVal buff As bytes, _
                                  Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            If Not talk.modify(directory + file, buff) Then
                raiseError(errorHandle.errorType.warning, "cannot modify file ", directory, file)
                Return False
            Else
                Return writefilelist(directory, file, talk)
            End If
        End Function

        Public Function existfile(ByVal directory As String, ByVal file As String, _
                                  Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            Return talk.seek(directory + file)
        End Function

        Public Function sizeoffile(ByVal directory As String, ByVal file As String, _
                                   ByRef size As UInt64, _
                                   Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalize(directory, talk)
            Return talk.sizeof(directory + file, size)
        End Function
    End Module
End Namespace
